﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using X.Core.Interfaces;

namespace X.Core.Bases
{
    public abstract class ModelBase : IModel
    {
        public ModelBase()
        {
            IsEnabled = true;
            Id = Guid.NewGuid();
        }

        #region 'Methods'
        /// <summary>
        /// 深克隆，将 source 的属性填充到当前实例。
        /// 只允许同一继承链的类型互相 Fill。
        /// </summary>
        /// <typeparam name="T">目标类型，必须为当前类型或其派生类</typeparam>
        /// <param name="source">源对象</param>
        public virtual (int,string) Fill<T>(T source,bool isCheckType = true,IEnumerable<string> ignorePropList = null) where T : IModel
        {
            if (source == null) return(0,null);
            if(isCheckType)
            {
                // 只允许同一继承链的类型互相 Fill
                var thisType = this.GetType();
                var sourceType = source.GetType();
                if (!thisType.IsAssignableFrom(sourceType) && !sourceType.IsAssignableFrom(thisType))
                    return (-1, "只能在同一继承链的类型间 Fill。");
            }
            var properties = source.GetType().GetProperties();
            foreach (var p in properties)
            {
                if (p.PropertyType.ToString().Contains("RelayCommand")) continue;
                if (p.PropertyType.ToString().Contains("ObservableCollection`1")) continue;
                if (p.PropertyType.ToString().Contains("List`1")) continue;
                if (ignorePropList != null)
                {
                    if (ignorePropList.Contains(p.Name)) continue;
                }
                p.SetValue(this, p.GetValue(source));
            }
            //// 填充共有属性
            //this.Id = source.Id;
            //this.InternalId = source.InternalId;
            //this.Index = source.Index;
            //this.Name = source.Name;
            //this.Label = source.Label;
            //this.Description = source.Description;
            //this.IsEnabled = source.IsEnabled;
            //this.IsModelInitialized = source.IsModelInitialized;
            //this.CanNotify = source.CanNotify;
            //this.IsChanged = source.IsChanged;
            return (0, null);
        }
        public virtual (int,string) Init(JObject obj = null)
        {
            return (0, null);
        }
        
        public virtual void Dispose()
        {

        }
        public virtual object Clone()
        {
            return MemberwiseClone();
        }
        public override string ToString()
        {
            return $"{Index},{Name},{Label}";
        }
        #endregion

        #region 'Fields'
        Guid _id;
        Guid _internalId;
        int _index;
        string _name;
        string _label;
        string _description;
        bool _isEnabled;
        bool _isModelInitialized;
        bool _canNotify = true;
        bool _isChanged;
        int _group;
        int _groupSub;
        #endregion

        #region 'Properties'
        public Guid Id { get => _id; set => SetProperty(ref _id, value); }
        public Guid InternalId { get => _internalId; set => SetProperty(ref _internalId, value); }
        public int Index { get => _index; set => SetProperty(ref _index, value); }
        public string Name { get => _name; set => SetProperty(ref _name, value); }
        public string Label { get => _label; set => SetProperty(ref _label, value); }
        public string Description { get => _description; set => SetProperty(ref _description, value); }
        public bool IsEnabled { get => _isEnabled; set => SetProperty(ref _isEnabled, value); }
        public bool IsModelInitialized { get => _isModelInitialized; set => SetProperty(ref _isModelInitialized, value); }
        public bool CanNotify { get => _canNotify; set => SetProperty(ref _canNotify, value); }
        public bool IsChanged { get => _isChanged; set => SetProperty(ref _isChanged, value); }
        public int Group { get => _group; set => SetProperty(ref _group, value); }
        public int GroupSub { get => _groupSub; set => SetProperty(ref _groupSub, value); }

        #endregion


        #region PropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            RaisePropertyChanged(propertyName);
            return true;
        }

        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            onChanged?.Invoke();
            RaisePropertyChanged(propertyName);
            return true;
        }

        public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        public virtual void OnPropertyChanged(PropertyChangedEventArgs args)
        {
            if (args.PropertyName != nameof(IsChanged))
            {
                IsChanged = true;
            }
            if (CanNotify)
            {
                this.PropertyChanged?.Invoke(this, args);
            }
        }

       


        #endregion PropertyChanged
    }
}
